package com.gitee.wsl.mvi

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.gitee.wsl.flow.create.datasource.DataSourceLoadState
import com.gitee.wsl.result.EntryResult
import com.gitee.wsl.result.sample.ValueResult
import com.gitee.wsl.result.onFailure
import com.gitee.wsl.result.onSuccess
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.MainCoroutineDispatcher
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.launch


abstract class BaseViewModel<A : Action, S : State, E : Effect> : ViewModel() {

    /**继承BaseViewModel需要实现state默认值*/
    abstract fun initialState(): S

    val _state by lazy {
        MutableStateFlow<EntryResult<S, Exception>>(value = ValueResult.Ok(initialState()))
    }

    val resultStateFlow: Flow<EntryResult<S, Exception>> by lazy{_state.asSharedFlow()}


    //val state: StateFlow<S> by lazy { _state.filterSuccess() }

    protected fun emitState(builder: suspend () -> S?) = viewModelScope.launch {
        builder()?.let {emitState(it)}
    }

    /**suspend 函数在flow或者scope中emit状态*/
    protected suspend fun emitState(state: S) = emitState(ValueResult.Ok(state))

    protected suspend fun emitState(state: EntryResult<S, Exception>) {
        _state.emit(state)
    }

    protected suspend fun <Exp:Exception> emitState(error: Exp) {
        emitState(ValueResult.Err(error))
    }

    protected suspend fun emitLoadingState() {
        emitState(DataSourceLoadState.idle<S>())
    }

    /**
     * [effect]事件带来的副作用，通常是一次性事件 例如：弹Toast、导航Fragment等
     */
    private val _effect = MutableSharedFlow<E>()
    val effect: SharedFlow<E> by lazy { _effect.asSharedFlow() }

    protected fun emitEffect(builder: suspend () -> E?) = viewModelScope.launch {
        builder()?.let { _effect.emit(it) }
    }

    protected suspend fun emitEffect(effect: E) = _effect.emit(effect)


    /**
     * action
     */
    private val _action = Channel<A>()

    //var replayState:S?=null

    init {
        viewModelScope.launch {
            _action.consumeAsFlow().collect {
                /*replayState：很多时候我们需要通过上个state的数据来处理这次数据，所以我们要获取当前状态传递*/
                onAction(it, replayState)
            }
        }
    }

    /** [actor] 用于在非viewModelScope外使用*/
    val actor: SendChannel<A> by lazy { _action }

    fun sendAction(action: A) = viewModelScope.launch {
        _action.send(action)
    }

    open fun onAction(action: A, currentState: EntryResult<S, Exception>?){
        currentState?.onSuccess {
            onAction(action,it)
        }?.onFailure {
            it.printStackTrace()
        }
    }

    /** 订阅事件的传入 onAction()分发处理事件 */
    protected abstract fun onAction(action: A, currentState: S?)


    /**
     * Launch a coroutine in the [viewModelScope] that will execute [block] on the same thread.
     * The [Dispatchers.Main.immediate] dispatcher will be used to avoid unnecessary thread jumps.
     * If [Dispatchers.Main.immediate] is not available in the target platform, then [Dispatchers.Main] will be used.
     */
    private fun threadSafeRunnerOnMain(block: suspend () -> Unit): Job {
        val dispatcher: MainCoroutineDispatcher =
            try {
                Dispatchers.Main.immediate
            } catch (e: UnsupportedOperationException) { // When Main.immediate is not available in the target platform
                Dispatchers.Main
            }
        return viewModelScope.launch(dispatcher + NonCancellable) { // This needs to be non-cancellable to avoid cancellation of the block in onCleared
            block()
        }
    }
}

val <S:State> BaseViewModel<*,S,*>.replayState: S?
    get() = _state.replayCache.firstOrNull()?.component1()

val <S:State> BaseViewModel<*,S,*>.replayResultState: EntryResult<S,Exception>?
    get() = _state.replayCache.firstOrNull()